package com.cksecurity.dao.impl;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.query.Query;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate5.HibernateCallback;
import org.springframework.orm.hibernate5.HibernateTemplate;
import org.springframework.stereotype.Repository;

import com.cksecurity.dao.GenericDao;
import com.cksecurity.util.PageBean;

@Repository("genericDao")
public class GernericDaoImpl implements GenericDao {

    @Autowired
    private HibernateTemplate hibernateTemplate;

    @Override
    public <T> T getObject(Class<T> t, Serializable id) {
        return hibernateTemplate.get(t, id);
    }

    @Override
    public <T> List<T> getAll(Class<T> t) {
        Session session = hibernateTemplate.getSessionFactory().getCurrentSession();
        CriteriaBuilder criteriaBuilder = session.getEntityManagerFactory().getCriteriaBuilder();
        CriteriaQuery<T> createQuery = criteriaBuilder.createQuery(t);
        Root<T> root = createQuery.from(t);
        CriteriaQuery<T> criteriaQuery = createQuery.select(root);
        Query<T> query = session.createQuery(criteriaQuery);
        return query.getResultList();
    }

    @Override
    public <T> List<T> findByHql(String hql, Class<T> t, Map<String, Object> params) {
        return this.find(hql, t, false, params, null);
    }

    @Override
    public <T> List<T> findByHqlPageable(String hql, Class<T> t, Map<String, Object> params, PageBean pageBean) {
        return this.find(hql, t, false, params, pageBean);
    }

    @Override
    public <T> List<T> findByHql(String hql, Class<T> t, Object... params) {
        return this.find(hql, t, false, null, params);
    }

    @Override
    public <T> List<T> findBySql(String sql, Class<T> t, Map<String, Object> params) {
        return this.find(sql, t, true, params, null);
    }

    @Override
    public <T> List<T> findBySqlPageable(String sql, Class<T> t, Map<String, Object> params, PageBean pageBean) {
        return this.find(sql, t, true, params, pageBean);
    }

    @Override
    public <T> List<T> findBySql(String sql, Class<T> t, Object... params) {
        return this.find(sql, t, true, null, params);
    }
    
    @Override
    public <T> void saveObject(Class<T> t) {
        hibernateTemplate.save(t);
    }

    @Override
    public <T> void updateObject(Class<T> t) {
        hibernateTemplate.update(t);
    }

    @Override
    public <T> void saveOrUpdateObject(Class<T> t) {
        hibernateTemplate.saveOrUpdate(t);
    }

    @SuppressWarnings("null")
    private <T> List<T> find(String sql, Class<T> t, boolean isSql, Map<String, Object> params, PageBean pageBean) {
        List<T> list = hibernateTemplate.execute(new HibernateCallback<List<T>>() {

            @Override
            public List<T> doInHibernate(Session session) throws HibernateException {
                
                Query<T> query = null;
                if (isSql)
                    session.createNativeQuery(sql, t);
                else
                    session.createQuery(sql, t);
                params.forEach((k, v) -> {
                    if (v instanceof Collection<?>)
                        query.setParameterList(k, (Collection<?>) v);
                    else if (v instanceof Object[])
                        query.setParameterList(k, (Object[]) v);
                    else
                        query.setParameter(k, v);
                });
                if (null != pageBean) {
                    query.setFirstResult((pageBean.getPageNum() - 1) * pageBean.getPageSize());
                    query.setMaxResults(pageBean.getPageSize());
                }
                return query.list();
            }
        });
        return list;

    }
    
    @SuppressWarnings("null")
    private <T> List<T> find(String sql, Class<T> t, boolean isSql, PageBean pageBean, Object... params) {
        List<T> list = hibernateTemplate.execute(new HibernateCallback<List<T>>() {

            @Override
            public List<T> doInHibernate(Session session) throws HibernateException {

                Query<T> query = null;
                if (isSql)
                    session.createNativeQuery(sql, t);
                else
                    session.createQuery(sql, t);
                int position = 0;
                for (Object o : params) {
                    query.setParameter(position, o);
                    position ++;
                }
                if (null != pageBean) {
                    query.setFirstResult((pageBean.getPageNum() - 1) * pageBean.getPageSize());
                    query.setMaxResults(pageBean.getPageSize());
                }
                return query.list();
            }
        });
        return list;

    }

}
